/*
 * @Author: ldm
 * @Date: 2021-04-14 14:30:12
 * @LastEditTime: 2021-09-18 11:32:08
 * @LastEditors: LDM
 * @Description: In User Settings Edit
 */
import qs from 'qs'
import type { AxiosRequestConfig, AxiosInstance, AxiosResponse, AxiosError } from 'axios'

import axios from 'axios'
// type MInterceptors = AxiosInstance['interceptors']
interface UploadFileParams {
  // Other parameters
  data?: any
  //   file字段名
  fileKey: string
  // file name
  fileName?: string
  file: File | Blob
  [key: string]: any
}
interface RequestOptions extends AxiosRequestConfig {
  showLoading?: string
  needToken?: boolean
}
/* 请求结果类型 */
export interface Result<T = any> {
  code: number
  // type: 'success' | 'error' | 'warning'
  msg: string
  data: T
}
export enum ContentTypeEnum {
  // json
  JSON = 'application/json;charset=UTF-8',
  // form-data qs
  FORM_URLENCODED = 'application/x-www-form-urlencoded;charset=UTF-8',
  // form-data  upload
  FORM_DATA = 'multipart/form-data;charset=UTF-8'
}

export class MAxios {
  private axiosInstance: AxiosInstance
  private readonly options: RequestOptions
  beforRequest: (config: RequestOptions) => RequestOptions

  afterRequest: (res: AxiosResponse<Result>) => any
  afterRequestError: (err: AxiosError<Result>) => any

  constructor(options: RequestOptions) {
    this.options = options
    this.axiosInstance = axios.create(options)
    this.beforRequest = (config) => config
    this.afterRequest = (res) => res
    this.afterRequestError = (err) => err
    this.setupInterceptors()
  }

  /**
   * @description:  Create axios instance
   */
  private createAxios(config: RequestOptions): void {
    this.axiosInstance = axios.create(config)
  }

  /**
   * @description:  Create axios instance
   */
  // private createInterceptors(): MInterceptors {
  //   return this.axiosInstance.interceptors
  // }

  getAxios(): AxiosInstance {
    return this.axiosInstance
  }

  /**
   * @description: 重新配置axios
   */
  configAxios(config: RequestOptions) {
    if (!this.axiosInstance) {
      return
    }
    this.createAxios(config)
  }

  /**
   * @description: 重新设置请求头
   */
  setHeader(headers: any): void {
    if (!this.axiosInstance) {
      return
    }
    Object.assign(this.axiosInstance.defaults.headers, headers)
  }

  /**
   * @description: 拦截器配置
   */
  private setupInterceptors() {
    // 请求前置拦截器
    this.axiosInstance.interceptors.request.use((config: RequestOptions) => {
      return this.beforRequest(config)
    }, undefined)

    // 请求前置拦截器 错误捕捉
    //   this.axiosInstance.interceptors.request.use(undefined, requestInterceptorsCatch);

    // 请求响应拦截器
    this.axiosInstance.interceptors.response.use(
      (res: AxiosResponse<any>) => this.afterRequest(res),
      (err: AxiosError<Result>) => this.afterRequestError(err)
    )

    // 请求响应拦截器 错误捕捉
    //   responseInterceptorsCatch &&
    //     isFunction(responseInterceptorsCatch) &&
    //     this.axiosInstance.interceptors.response.use(undefined, responseInterceptorsCatch);
  }

  /**
   * @description:  文件上传
   */
  uploadFile<T = any>(config: RequestOptions, params: UploadFileParams): Promise<T> {
    const formData = new window.FormData()
    if (params.data) {
      Object.keys(params.data).forEach((key) => {
        if (!params.data) return
        const value = params.data[key]
        if (Array.isArray(value)) {
          value.forEach((item) => {
            formData.append(`${key}[]`, item)
          })
          return
        }

        formData.append(key, params.data[key])
      })
    }

    formData.append(params.fileKey || 'file', params.file, params.filename)

    return this.axiosInstance.request({
      ...config,
      method: 'POST',
      data: formData,
      headers: {
        'Content-type': ContentTypeEnum.FORM_DATA
      }
    })
  }

  // support form-data
  supportFormData(config: RequestOptions) {
    const headers = this.options?.headers
    const contentType = headers?.['Content-Type'] || headers?.['content-type']

    if (
      contentType !== ContentTypeEnum.FORM_URLENCODED ||
      !Reflect.has(config, 'data') ||
      config.method?.toUpperCase() === 'GET'
    ) {
      return config
    }

    return {
      ...config,
      data: qs.stringify(config.data)
    }
  }

  get<T = any>(config: RequestOptions, options?: RequestOptions): Promise<T> {
    return this.request({ ...config, method: 'GET' }, options)
  }

  post<T = any>(config: RequestOptions, options?: RequestOptions): Promise<T> {
    return this.request({ ...config, method: 'POST' }, options)
  }

  put<T = any>(config: RequestOptions, options?: RequestOptions): Promise<T> {
    return this.request({ ...config, method: 'PUT' }, options)
  }

  delete<T = any>(config: RequestOptions, options?: RequestOptions): Promise<T> {
    return this.request({ ...config, method: 'DELETE' }, options)
  }

  request<T = any>(config: RequestOptions, options?: RequestOptions): Promise<T> {
    let conf: RequestOptions = { ...config }
    Object.assign(conf, options || {})
    conf = this.supportFormData(conf)
    return new Promise((resolve, reject) => {
      this.axiosInstance
        .request<any, AxiosResponse<Result>>(conf)
        .then((res: AxiosResponse<Result>) => {
          resolve(res as unknown as Promise<T>)
        })
        .catch((e: Error) => {
          reject(e)
        })
      // this.axiosInstance
      //   .request<any, AxiosResponse<Result>>(conf)
      //   .then((res: AxiosResponse<Result>) => {
      //     resolve((res as unknown) as Promise<T>)
      //   })
      //   .catch((e: Error) => {
      //     reject(e)
      //   })
    })
  }
}
